package device

import (
	"encoding/json"
	"fmt"
	"gitee.com/sansaniot/ssiot-core/facade/env"
	js "github.com/dop251/goja"
	"github.com/forestgiant/sliceutil"
	"math/rand"
	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/query"
	"ssdevice/internal/rule/model"
	"strings"
	"time"
)

const PassThroughKey = "passThrough"

func DevFuncRegister(call js.ConstructorCall) *js.Object {
	call.This.Set("realTimeData", GetRealData)
	call.This.Set("logoutTimeExceed", SendLogoutTopic)
	call.This.Set("passThroughTo", passThroughTo)
	return nil
}

// 透传数据到其它设备
func passThroughTo(context map[string]interface{}, devSn string, data map[string]interface{}) {
	// data中必须包含passThrough
	if _, ok := data[PassThroughKey]; !ok {
		env.Log.Debugf("rule-engine: pass through fail . dev %s has no passThrough key", devSn)
		return
	}

	// 获得网关
	devCache, _ := env.Cache.HashGet(constant.DeviceEasyCache, devSn)
	if len(devCache) == 0 {
		env.Log.Debugf("rule-engine: pass through fail . dev %s has no cache", devSn)
		return
	}

	devCacheMap := utils.BytesToMap([]byte(devCache))
	if _, ok := devCacheMap["gateway"]; !ok {
		env.Log.Debugf("rule-engine: pass through fail . dev %s has no gateway cache", devSn)
		return
	}

	if _, ok := devCacheMap["gateway"].(string); !ok {
		env.Log.Debugf("rule-engine: pass through fail . dev %s has wrong gateway cache type", devSn)
		return
	}

	var topicTo, to, gatewaySn string
	gatewaySn = devCacheMap["gateway"].(string)

	if gatewaySn == "" {
		to = devSn
		topicTo = devSn
	} else {
		to = devSn
		topicTo = gatewaySn
	}

	// 下发数据
	topic := fmt.Sprintf("/iot/%s/%s/sensor/set", topicTo, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(100000)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = data
	contentBytes, _ := json.Marshal(content)
	// 下发
	mqtt.MqttClient.Publish(topic, contentBytes)
	env.Log.Debugf("rule-engine: pass through data from %s to %s", context["sn"].(string), devSn)
}

func checkSn(sn, ruleId string) bool {
	// 获得规则信息
	rule := &model.Rule{}
	rule.Orm = env.Db
	_ = rule.GetById(ruleId)
	ruleSns := strings.Split(rule.DevSn, ",")

	if sliceutil.Contains(ruleSns, sn) {
		return true
	}

	productIds := strings.Split(rule.ProductId, ",")
	//查询设备对应产品编码
	devCache, _ := env.Cache.HashGet(constant.DeviceEasyCache, sn)
	if len(devCache) == 0 {
		return false
	}
	info := utils.BytesToMap([]byte(devCache))
	productModel := utils.If(info["productModel"] != nil, info["productModel"], "").(string)
	if len(productModel) == 0 {
		return false
	}
	if sliceutil.Contains(productIds, productModel) {
		return true
	}
	return false
}

// GetRealData 规则引擎获取实时数据函数
func GetRealData(context map[string]interface{}) map[string]map[string]interface{} {
	result := make(map[string]map[string]interface{})
	ruleId, _ := context["_reserve_rule_id"].(string)
	sn, _ := context["sn"].(string)

	// 不在规则中的sn不能统计
	if !checkSn(sn, ruleId) {
		return nil
	}
	// 查询实时数据
	sysDevVariable := &models.SysDevVariable{}
	sysDevVariable.Orm = env.Db
	list := make([]models.SysDevVariable, 0)
	if err := sysDevVariable.FindList(&list, cDto.MakeCondition(query.SysDevVariableQuery{Sn: sn})); err != nil {
		return result
	}
	// 返回实时数据
	for _, v := range list {
		result[v.Key] = utils.DataToMap(v)
	}
	return result
	// 查询缓存  TODO

	// 不存在则查库

	// 更新缓存 并设置过期时间
}

// SendLogoutTopic 页面发送logout主题，用于设备超时下线判断
func SendLogoutTopic(context map[string]interface{}, logoutSecond int) map[string]interface{} {
	result := make(map[string]interface{})
	ruleId, _ := context["_reserve_rule_id"].(string)
	sn, _ := context["sn"].(string)

	// 不在规则中的sn不能统计
	if !checkSn(sn, ruleId) {
		return result
	}
	//查询设备
	sysDev := models.SysDev{}
	sysDev.Orm = env.Db
	if err := sysDev.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: sn})); err != nil {
		return result
	}
	if len(sysDev.Sn) == 0 || sysDev.LastLiveTime == nil {
		return result
	}
	gateway := sysDev.GatewaySn
	lastLiveTime := time.Time(*sysDev.LastLiveTime)
	now := time.Now()
	now = now.Truncate(time.Second)
	logoutTime := now.Add(time.Duration(-logoutSecond) * time.Second)
	if logoutTime.After(lastLiveTime) && sysDev.OnlineStatus == "online" {
		// 发送登出包
		fmt.Print(gateway)
		logoutTopic := fmt.Sprintf("/iot/%s/%s/logout", constant.TopicPlaceholder, gateway)
		logoutData := map[string]interface{}{
			"from": sn,
		}
		mqtt.MqttClient.Publish(logoutTopic, utils.MapToBytes(logoutData))
	}
	return result
}
